package org.jvm.rtda.heap.classLoader;

import org.jvm.classpath.BootClassPath;
import org.jvm.rtda.Object;
import org.jvm.rtda.heap.Klass;
import org.jvm.rtda.thread.Thread;

import java.util.*;

/**
 * @author 海燕
 * @date 2023/3/23
 */
public class KlassLoaderRegister {

    /**
     * 注册所有java类加载器
     * key->java类加载器对应的java实例  value->java类加载器
     */
    private static Map<Object, JavaKlassLoader> javaklassLoaderMap = new HashMap<>();

    /**
     * 根类加载器
     */
    private static BootKlassLoader bootKlassLoader;

    /**
     * 扩展类加载器
     */
    private static JavaKlassLoader extKlassLoader;

    /**
     * 系统类加载器
     */
    private static JavaKlassLoader appKlassLoader;

    /**
     * 初始化根类加载器
     *
     * @param classPath
     */
    public static void initBootKlassLoader(BootClassPath classPath) {
        bootKlassLoader = new BootKlassLoader(classPath);
        bootKlassLoader.init();
    }

    /**
     * 初始化扩展类加载器和用户类加载器
     *
     * @param extClassLoader
     * @param appClassLoader
     */
    public static void initExtAndAppClassLoader(Object extClassLoader, Object appClassLoader) {
        extKlassLoader = getJavaKlassLoader(extClassLoader);
        appKlassLoader = getJavaKlassLoader(appClassLoader);
    }

    /**
     * 根据java类加载器实例返回JVM中对应的java类加载器，如果没有则新建并注册
     *
     * @param javaClassLoaderInstance
     * @return
     */
    public static JavaKlassLoader getJavaKlassLoader(Object javaClassLoaderInstance) {
        JavaKlassLoader javaKlassLoader = javaklassLoaderMap.get(javaClassLoaderInstance);
        if (javaKlassLoader != null) {
            return javaKlassLoader;
        }
        javaKlassLoader = new JavaKlassLoader(javaClassLoaderInstance);
        javaklassLoaderMap.put(javaClassLoaderInstance, javaKlassLoader);
        return javaKlassLoader;
    }

    public static BootKlassLoader getBootKlassLoader() {
        return bootKlassLoader;
    }

    public static JavaKlassLoader getExtKlassLoader() {
        return extKlassLoader;
    }

    public static JavaKlassLoader getAppKlassLoader() {
        return appKlassLoader;
    }

    /**
     * 先寻找已经加载到方法区的方法，如果找不到直接调用系统类加载器进行加载
     *
     * @param thread
     * @param klassName
     * @return
     */
    public static Klass loadKlass(String klassName, Thread thread) {
        Klass klass = findLoadedKlass(klassName);
        if (klass != null) {
            return klass;
        }
        return getAppKlassLoader().loadKlass(klassName, thread);
    }

    /**
     * 从根类加载器、扩展类加载器、系统加载器的方法区中寻找已经加载过的类
     * 这三个加载器在双亲委托机制的保证下，不会有重名的类
     *
     * @param klassName
     * @return
     */
    private static Klass findLoadedKlass(String klassName) {
        AbstractKlassLoader[] klassLoaders = new AbstractKlassLoader[]{bootKlassLoader, extKlassLoader, appKlassLoader};
        for (AbstractKlassLoader klassLoader : klassLoaders) {
            if (klassLoader == null) {
                continue;
            }
            Klass loadedKlass = klassLoader.findLoadedKlass(klassName);
            if (loadedKlass != null) {
                return loadedKlass;
            }
        }
        return null;
    }


}
